package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("StoremethodService")
@Transactional

public class StoremethodServiceImpl extends CmiEmaService implements StoremethodService {
	public StoremethodServiceImpl() {
	}

	@Transactional
	public Storemethod saveStoremethod(Storemethod storemethod) {
		Set<Food> existingFoodsForStoremethod = new HashSet<Food>();

		Set<Food> selectedFoodsForStoremethod = storemethod.getFoodsForStoremethod();

		if (storemethod.getId() == null) {

			Storemethod existingStoremethod = storemethodDAO.findStoremethodByPhysicalPrimaryKey(storemethod.getName());

			if (isEquals(storemethod, existingStoremethod)) {
				return existingStoremethod;
			}

			storemethod = storemethodDAO.store(storemethod);
			storemethodDAO.flush();

			for (Food relatedFood : selectedFoodsForStoremethod) {
				relatedFood.setStoremethod(storemethod);
				foodDAO.store(relatedFood);
			}
		} else {
			Storemethod existingStoremethod = storemethodDAO.findStoremethodByPrimaryKey(storemethod.getId());

			existingFoodsForStoremethod = existingStoremethod.getFoodsForStoremethod();

			Set<Food> prepareDeleteFoodsForStoremethod = new HashSet<Food>();
			Set<Food> prepareAddFoodsForStoremethod = new HashSet<Food>();

			prepareDeleteFoodsForStoremethod.addAll(selectedFoodsForStoremethod);
			prepareDeleteFoodsForStoremethod.addAll(existingFoodsForStoremethod);

			prepareAddFoodsForStoremethod.addAll(selectedFoodsForStoremethod);
			prepareAddFoodsForStoremethod.addAll(existingFoodsForStoremethod);

			prepareDeleteFoodsForStoremethod.removeAll(selectedFoodsForStoremethod);
			prepareAddFoodsForStoremethod.removeAll(existingFoodsForStoremethod);

			for (Food relatedFood : prepareAddFoodsForStoremethod) {
				relatedFood.setStoremethod(storemethod);
				foodDAO.store(relatedFood);
			}

			for (Food relatedFood : prepareDeleteFoodsForStoremethod) {
				relatedFood.setStoremethod(null);
				foodDAO.store(relatedFood);
			}
			storemethod = storemethodDAO.store(storemethod);
			storemethodDAO.flush();
		}

		foodDAO.flush();
		return storemethod;
	}

	public void deleteStoremethod(Storemethod storemethod) {
		storemethodDAO.remove(storemethod);
		storemethodDAO.flush();
	}

	@Transactional
	public void deleteStoremethods(Set<Integer> deleteStoremethodsId) {
		Query query = storemethodDAO.createNamedQuery("deleteStoremethodsById", -1, -1);
		query.setParameter(1, deleteStoremethodsId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Storemethod> loadStoremethods() {
		return storemethodDAO.findAllStoremethods();

	}

	@Transactional
	public Set<Storemethod> loadReferenceStoremethods(Set<Storemethod> storemethods) {
		Iterator<Storemethod> storemethodsIterator = storemethods.iterator();
		Set<Storemethod> referencedStoremethods = new HashSet<>();
		while (storemethodsIterator.hasNext()) {
			Storemethod currentStoremethod = storemethodsIterator.next();

			if (!currentStoremethod.getFoodsForStoremethod().isEmpty()) {
				referencedStoremethods.add(currentStoremethod);
				continue;
			}
		}
		return referencedStoremethods;
	}

	public Set<Storemethod> loadStoremethodsByIdSet(Set<Integer> storemethodIdSet) {

		Iterator<Integer> storemethodIdIterator = storemethodIdSet.iterator();
		Set<Storemethod> storemethodSet = new HashSet<>();
		while (storemethodIdIterator.hasNext()) {
			Integer id = storemethodIdIterator.next();
			Storemethod currentStoremethod = storemethodDAO.findStoremethodById(id);
			storemethodSet.add(currentStoremethod);
		}
		return storemethodSet;
	}

	@Transactional
	public List<Storemethod> findAllStoremethods(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Storemethod>(storemethodDAO.findAllStoremethods(startResult, maxRows));
	}

	@Transactional
	public Storemethod findStoremethodByPrimaryKey(Integer id) {
		return storemethodDAO.findStoremethodByPrimaryKey(id);
	}

	@Transactional
	public Integer countStoremethods() {
		return ((Long) storemethodDAO.createQuerySingleResult("select count(o) from Storemethod o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countStoremethods(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Storemethod.class);
		return ((Long) storemethodDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countStoremethodsByName(String queryName) {
		Query query = storemethodDAO.createNamedQuery("countStoremethodsByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countStoremethodsByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Storemethod.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadStoremethodObjectReferencedSet(Storemethod storemethod) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();

		Set<Integer> foodsIds = new HashSet<Integer>();
		for (Food obj : storemethod.getFoodsForStoremethod()) {

			foodsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodsIds", foodsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadStoremethodClassReferencedSet(Storemethod storemethod) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("foods", foodDAO.findAllFoods());

		return classReferencedSetMap;
	}

	public boolean isEquals(Storemethod storemethod, Object obj) {
		if (obj == storemethod)
			return true;
		if (!(obj instanceof Storemethod))
			return false;
		Storemethod comparedStoremethod = (Storemethod) obj;
		if (!CheckPropertyEqual(storemethod.getName(), comparedStoremethod.getName())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

